package nickthissen.iracing.parsing;

import java.util.ArrayList;
import java.util.List;

import nickthissen.iracing.datamodels.Result;
import nickthissen.iracing.datamodels.Thread;
import nickthissen.iracing.datamodels.ThreadList;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
import org.jsoup.select.Elements;

import android.text.TextUtils;
import android.util.Log;

public class ThreadListParser extends Parser
{
	@Override
	protected String getLogName() {
		return "ThreadListParser";
	}

    @Override
    protected Result createDefaultResultObject()
    {
        Result result = new Result();
        ThreadList threadlist = new ThreadList();
        threadlist.Threads = new ArrayList<Thread>();
        result.Object = threadlist;
        result.Message = "Error parsing thread list.";
        return result;
    }

	@Override
	public Result doParse(String source)
	{
        Result result = new Result();
        ThreadList threadlist = new ThreadList();
        threadlist.Threads = new ArrayList<Thread>();

        log("Start jsoup parsing thread list");
        Document doc = Jsoup.parse(source);
        log("Finished jsoup parsing thread list.");

        log("Start parsing pagination.");
        parsePagination(threadlist, doc);
        log("Finished parsing pagination.");

        log("Start parsing CanPost");
        parseCanPost(threadlist, doc);
        log("Finished parsing CanPost");

        Element tableNode = doc.getElementById("forum");

        // Loop through rows of table
        ArrayList<Element> rows = Parsing.selectChildren(tableNode.select("tbody").first(), "tr"); // tableNode.select("tr");
        for (int trIndex = 0; trIndex < rows.size(); trIndex++)
        {
            Element tr = rows.get(trIndex);
            boolean rowIsParsed = false;

            // The top row (column headers) is the only row with attributes - skip it
            if (tr.attributes().size() > 0) continue;

            log("Start parsing table row: " + tr.toString());

            // Loop through columns
            ArrayList<Element> columns = Parsing.selectChildren(tr, "td[class]");// tr.select("td[class]");
            for (int tdIndex = 0; tdIndex < columns.size(); tdIndex++)
            {
                Element td = columns.get(tdIndex);

                // If row already parsed, skip it
                if (rowIsParsed) continue;

                log("Start parsing thread.");
                Thread thread = parseThread(tr);
                if (thread != null)
                {
                    threadlist.Threads.add(thread);
                    log("Thread created.");
                }
                else
                {
                    log("Thread NOT created.");
                }
                rowIsParsed = true;
            }
        }

        parseWatching(doc, threadlist);

		result.Success = true;
		result.Object = threadlist;

		return result;
	}

	private String trimText(String text)
	{
		text = text.replace("\t", "").replace("\r", "").replace("\n", "");
		return text;
	}

    private void parseCanPost(ThreadList threadList, Document doc)
    {
        Elements postButtons = doc.select("div[class=contentBar] > a[id=postANewTopic]");
        threadList.CanPost = postButtons.size() > 0;
    }

	private Thread parseThread(Element node)
	{
		Thread thread = new Thread();

		Elements nodeElements = node.select("td[class]");
		Element[] nodes = nodeElements.toArray(new Element[nodeElements.size()]);
		if (nodes.length != 6)
        {
            log("Not 6 td nodes.");
            return null;
        }

		Element iconNode = nodes[0];
        log("Icon node: " + iconNode.html());
		parseIcon(thread, iconNode);

		Element topicNode = nodes[1];
        log("Topic node: " + topicNode.html());
		Elements linkElements = topicNode.select("a");
        log("Link nodes: " + linkElements.html());
		Element[] linkNodes = linkElements.toArray(new Element[linkElements.size()]);
		
		Elements imgElements = topicNode.select("img");
        if (imgElements.size() > 0)
        {
            Element img = imgElements.first();
            String src = img.attr("src");
            if (!TextUtils.isEmpty(src))
            {
                if (src.endsWith("iconClip.png")) {
                    thread.HasAttachments = true;
                }
                if (src.endsWith("iconPoll.png")) {
                    thread.IsPoll = true;
                }
            }
        }
		
		if (linkNodes.length > 0)
		{
			Element titleNode = linkNodes[0];

            log("Title node: " + titleNode.html());

			//TODO: De-entitize
			thread.Url = titleNode.attr("href");
			thread.Id = Integer.parseInt(Parsing.getFilenameWithoutExtension(thread.Url).trim());
			thread.Title = titleNode.text();
			
			// Get last page
			if (linkNodes.length > 1)
			{
				Element lastPageNode = linkNodes[linkNodes.length-1];
				thread.LastPageUrl = lastPageNode.attr("href");
				thread.PageCount = Integer.parseInt(lastPageNode.text().trim());
			}
			else
			{
				thread.LastPageUrl = "";
				thread.PageCount = 1;
			}
		}
		
		Element authorNode = nodes[2];
        log("Author node: " + authorNode.html());
		Node linkNode = findFirstLinkNode(authorNode);
		if (linkNode != null)
		{
			thread.AuthorUrl = linkNode.attr("href");
			thread.Author = ((Element)linkNode).text();
		}
		
		parseRepliesViews(thread, nodes);
		
		Element lastNode = nodes[5];
        log("Last node: " + lastNode.html());
		parseLastReply(thread, lastNode);
		
		return thread;
	}
	
	private Node findFirstLinkNode(Element node)
	{
		return findFirstLinkNode(node, 5);
	}	
	
	private Node findFirstLinkNode(Element node, int limit)
	{
		Node child = node.child(0);
		if (child == null) return null;
		if (child.nodeName().equals("a")) return child;
		
		int count = 0;
		while (count <= limit && child != null)
		{
			child = child.nextSibling();
			if (child != null && child.nodeName().equals("a")) return child;
		}
		
		return null;
	}
	
	private void parseIcon(Thread thread, Element node)
	{		
		Node imgNode = node.childNode(0);
		if (imgNode == null || !imgNode.nodeName().equals("img")) return;
		
		String path = imgNode.attr("src");
		String name = Parsing.getFilenameWithoutExtension(path);
		if (name != null) thread.IconName = name.toLowerCase().trim();
	}
	
	private void parseRepliesViews(Thread thread, Element[] nodes)
	{
		String repliesString = nodes[3].text();
		String viewsString = nodes[4].text();
		
		thread.Replies = 0;
		thread.Views = 0;
		if (TextUtils.isDigitsOnly(repliesString)) {
			thread.Replies = Integer.parseInt(repliesString.trim());
		}
		if (TextUtils.isDigitsOnly(viewsString)) {
			thread.Views = Integer.parseInt(viewsString.trim());
		}
	}
	
	private void parseLastReply(Thread thread, Element node)
	{
		Element[] children = node.children().toArray(new Element[0]);
		Element scriptNode = children[0];
		Element userLinkNode = children[2];
		Element linkNode = children[3];

        log("Scriptnode: " + scriptNode);
        log("UserLinkNode: " + userLinkNode);
        log("LinkNode: " + linkNode);

        String dateString = scriptNode.toString();
        long unixMs = this.parseDateTimeScript(dateString);
		
		thread.LastMessageTime = unixMs;
		thread.LastMessageUser = userLinkNode.text().trim();
		thread.LatestReplyUrl = linkNode.attr("href");				
	}

    private void parseWatching(Document doc, ThreadList threadlist)
    {
        Elements watch = doc.select("div[class=contentBar] > a[id=watchThisForum]");
        Elements unwatch = doc.select("div[class=contentBar] > a[id=stopWatching]");
        if (watch.size() == 0 && unwatch.size() == 1)
            threadlist.IsWatching = true;
        else
            threadlist.IsWatching = false;
    }
}
